package top.wardyan.wtask.feishu.api;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
// import org.json.JSONObject;
import java.nio.charset.StandardCharsets;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;

import com.alibaba.fastjson.JSONObject;
import com.google.gson.JsonParser;
import com.jfinal.aop.Aop;
import com.jfinal.kit.Prop;
import com.jfinal.plugin.activerecord.Record;
import com.lark.oapi.Client;
import com.lark.oapi.core.request.RequestOptions;
import com.lark.oapi.core.utils.Jsons;
import com.lark.oapi.service.contact.v3.model.GetUserReq;
import com.lark.oapi.service.contact.v3.model.GetUserResp;
import com.lark.oapi.service.contact.v3.model.GetUserRespBody;
import com.lark.oapi.service.im.v1.enums.ListMessageSortTypeEnum;
import com.lark.oapi.service.im.v1.model.CreateMessageReq;
import com.lark.oapi.service.im.v1.model.CreateMessageReqBody;
import com.lark.oapi.service.im.v1.model.CreateMessageResp;
import com.lark.oapi.service.im.v1.model.ListMessageReq;
import com.lark.oapi.service.im.v1.model.ListMessageResp;
import com.lark.oapi.service.im.v1.model.Message;

import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
import top.wardyan.base.common.annotation.Service;
import top.wardyan.base.common.config.ConfigProp;
import top.wardyan.base.common.db.kits.ProcKit;
import top.wardyan.base.common.db.po.WRecord;
import top.wardyan.base.common.util.DateUtil;
import top.wardyan.wtask.feishu.vo.FeishuTask;
import top.wardyan.wtask.feishu.vo.FeishuTaskItem;
import top.wardyan.wtask.service.IProcService;

@Service
public class FeishuMsgBusiness {

	private static final cn.hutool.log.Log log = cn.hutool.log.Log.get();

	private static final Prop PROP = ConfigProp.load();

	private static final String APPID = PROP.get("feishu.app_id");
	private static final String SECRET = PROP.get("feishu.app_secret");
	private static final String CONTAINERID = PROP.get("feishu.container_id");
	private static final Integer PAGESIZE = PROP.getInt("feishu.pagesize", 50);
	private static final String USERINFO_URL = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal/";
	private static final Integer MODE = PROP.getInt("feishu.mode", 1);
	private static final TimedCache<String, String> TIMECACHES = CacheUtil.newTimedCache(1000*60*60) ;
	
	public String getToken() {
		cn.hutool.cache.CacheUtil.newTimedCache(0);
		String rs = "";
	    try {
	        URL url = new URL(USERINFO_URL);
	        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
	        conn.setRequestMethod("POST");
	        conn.setRequestProperty("Content-Type", "application/json; utf-8");
	        conn.setRequestProperty("Accept", "application/json");
	        conn.setDoOutput(true);

	        String jsonInputString = "{\"app_id\": \"" + APPID + "\", \"app_secret\": \"" + SECRET + "\"}";

	        try (OutputStream os = conn.getOutputStream()) {
	            byte[] input = jsonInputString.getBytes("utf-8");
	            os.write(input, 0, input.length);           
	        }

	        try (BufferedReader br = new BufferedReader(
	                new InputStreamReader(conn.getInputStream(), "utf-8"))) {
	            StringBuilder response = new StringBuilder();
	            String responseLine = null;
	            while ((responseLine = br.readLine()) != null) {
	                response.append(responseLine.trim());
	            }
	            JSONObject jsonResponse = JSONObject.parseObject(response.toString());
	            rs = jsonResponse.getString("tenant_access_token");
	        }
	    } catch (Exception e) {
	        log.error(e);
	    }
	    return rs;
	}
	
	/**
	 * 从飞书根据用户ID获取姓名
	 * @param uid
	 * @return
	 */
	public String getManNameFromFeishu(String uid) {
		String rs = uid;
		// 构建client
		Client client = Client.newBuilder(APPID, SECRET).build();

		// 创建请求对象
		GetUserReq req = GetUserReq.newBuilder()
			.userId(uid)
			.build();
		
		String token = TIMECACHES.get("FEISHU_TOKEN", new Func0<String>() {

			@Override
			public String call() throws Exception {
				return getToken();
			}
			
		});

		// 发起请求
		GetUserResp resp;
		try {
			resp = client.contact().user().get(req, RequestOptions.newBuilder()
				// .userAccessToken("u-ca2Ppk60tf_UMPAxUN_Darg50uYBg4l9OG20k0Q00Dbe")
				.tenantAccessToken(token)
				.build());

			// 处理服务端错误
			if(!resp.success()) {
//				System.out.println(String.format("code:%s,msg:%s,reqId:%s, resp:%s",
//					resp.getCode(), resp.getMsg(), resp.getRequestId(), Jsons.createGSON(true, false).toJson(JsonParser.parseString(new String(resp.getRawResponse().getBody(), StandardCharsets.UTF_8)))));
				log.error(new String(resp.getRawResponse().getBody(), StandardCharsets.UTF_8));
				return rs;
			}
			// 业务数据处理
			GetUserRespBody b = resp.getData();
			rs = b.getUser().getName();
			String name = rs;
			ProcKit.exec("yp_feishu_saveuser", new HashMap<String,Object>(){{
				this.put("pMan", uid);
				this.put("pName", name);
			}});
//			System.out.println(Jsons.DEFAULT.toJson(resp.getData()));
//			resp.getData();
		} catch (Exception e) {
			log.error(e);
		}


		return rs;
	}
	
	/**
	 * 根据飞书uid获取姓名, 优先从数据库取,如果数据库中没有再从飞书下载
	 * @param uid
	 * @return
	 */
	public String getManName(String uid) {
		String rs = uid;
		
			
		rs = TIMECACHES.get(uid, new Func0<String>() {

			@Override
			public String call() throws Exception {
				String rs = uid;
				WRecord rcs = ProcKit.query("yp_feishu_finduser", new HashMap<String,Object>(){{
					this.put("pMan", uid);
				}});
				if(rcs != null) {
					Record r = rcs.getFirst();
					rs = r.getStr("xName");
				}
				return rs;
			}
			
		});

		if(rs == null) {
			if(uid != null && uid.startsWith("ou")) {
				rs = this.getManNameFromFeishu(uid);

			}
		}
		if(rs == null) {
			rs = uid;
		}
		return rs;
	}
	
	public Timestamp getPreMsgTime() {
		Timestamp rs = DateUtil.range(1);	// 默认是今天开始
		WRecord rcs = ProcKit.query("yp_feishu_getpremsgtime", new HashMap<String,Object>(){{}});
		if(rcs != null) {
			Record r = rcs.getFirst();
			//  = r.getTimestamp("rResult");
			rs = DateUtil.parse(r.getStr("rResult"));
		}
		return rs;
	}
	

	/**
	 * 自动下载飞书消息
	 */
	public void spy() {
		// 自动重拉最近一周内的数据
		Timestamp date = DateUtil.range(17);
		this.spy(date, false, "");
	}
	
	/**
	 * 从某个日期开始下载飞书消息
	 * @param begin 开始时间 (只精确到秒,不含那一秒)
	 * @param force 强制重导
	 */
	public Integer spy(Timestamp begin, Boolean force, String pagetoken) {
		Integer cnt = 0;
		
		// SDK 使用文档：https://github.com/larksuite/oapi-sdk-java/tree/v2_main
		// 复制该 Demo 后, 需要将 "YOUR_APP_ID", "YOUR_APP_SECRET" 替换为自己应用的 APP_ID, APP_SECRET.
		// 以下示例代码是根据 API 调试台参数自动生成，如果存在代码问题，请在 API 调试台填上相关必要参数后再使用
		// 构建client
		Client client = Client.newBuilder(APPID, SECRET).build();

		// 会不会有问题, 陷入死循环?
		if(begin == null) {
			begin = this.getPreMsgTime();
		}
		
		// Integer second = Float.valueOf(begin.getTime() / 1000).intValue() + 1;
		Long second = begin.getTime() / 1000 + 1;
		//Console.log("****", DateUtil.format(begin, DateUtil.FULL), Float.valueOf(begin.getTime() / 1000).intValue(), second);
		begin = new Timestamp(begin.getTime() + 1000L);
		//Console.log("****", DateUtil.format(begin, DateUtil.FULL));
		// 创建请求对象
		ListMessageReq req = ListMessageReq.newBuilder()
			.containerIdType("chat")
			.containerId(CONTAINERID)
			.startTime(String.valueOf(second))
			.pageSize(PAGESIZE)
			// .sortType(ListMessageReq.Builder.)
			.sortType(ListMessageSortTypeEnum.BYCREATETIMEASC)
			.pageToken(pagetoken)
			.build();

		// 发起请求
		ListMessageResp resp;
		try {
			resp = client.im().message().list(req);

			// 处理服务端错误
			if(!resp.success()) {
//				System.out.println(String.format("code:%s,msg:%s,reqId:%s, resp:%s",
//					resp.getCode(), resp.getMsg(), resp.getRequestId(), Jsons.createGSON(true, false).toJson(JsonParser.parseString(new String(resp.getRawResponse().getBody(), StandardCharsets.UTF_8)))));
				log.error(new String(resp.getRawResponse().getBody(), StandardCharsets.UTF_8));
				return cnt;
			}

			// 业务数据处理
			// System.out.println(Jsons.DEFAULT.toJson(resp.getData()));
			Message[] items = resp.getData().getItems();
			
			if(items != null && items.length > 0) {
				pagetoken = resp.getData().getPageToken();
				for(Message item : items) {
					// Console.log(item);
					Timestamp createTime = new Timestamp(Long.valueOf(item.getCreateTime()));
					Timestamp updateTime = new Timestamp(Long.valueOf(item.getUpdateTime()));
					// 
					if(updateTime.getTime() > begin.getTime()) {
						begin = updateTime;
					}
					String uid = item.getSender().getId();
					String man = this.getManName(uid);
					String msgtype = item.getMsgType();
					String msgid = item.getMessageId();
					if(!"text".equals(msgtype)){
						log.error("不支持的消息类型:{}", JSONObject.toJSONString(item));
						continue;
					}
					// item.getBody();
					
					try {
						String bodyContent = item.getBody().getContent();
						JSONObject jsonobject = JSONObject.parseObject(bodyContent);
						String text = jsonobject.getString("text");
						
						// Console.log(createTime, updateTime, man, text);
						this.deal(createTime, updateTime, man, text, msgid);
					}catch(Exception ex) {
						log.error(ex);
					}
					
					
					
					
					cnt += 1;
				}
				if(cnt > 0) {
					// 如果当前页有数据, 就递归调用
					Integer delay = 1;
					while(delay > 0) {
						Console.log("休眠", delay, "秒后继续下一页...");
						ThreadUtil.sleep(1000);
						delay -= 1;
					}
					spy(begin, force, pagetoken);
				}
			}else {
				// 应该是没有更多消息了
				log.info("没有更多的消息了");
			}
			
		} catch (Exception e) {
			log.error(e);
		}

		return cnt;
				
	}

	/**
	 * 检查某人某天的飞书记录是否有变更需要保存
	 * @param date
	 * @param man
	 */
	private Boolean feishuTaskIsChanged(Timestamp date, String man, Timestamp lastUpdate) {
		Boolean rs = true;
		
		WRecord rcs = ProcKit.exec("yp_feishu_getischange", new HashMap<String,Object>(){{
			this.put("pDate", date);
			this.put("pMan", man);
			this.put("pLastUpdate", lastUpdate);
		}});
		if(rcs != null) {
			Record r = rcs.getFirst();
			Integer ischanged = r.getInt("rResult");
			if(ischanged != null && ischanged == 0) {
				rs = false;
			}
		}
		return rs;
	}
	

	/**
	 * 重置某人某天的飞书任务记录
	 * @param date
	 * @param man
	 */
	private void clearDateMan(Timestamp date, String man) {
		ProcKit.exec("yp_feishu_cleardateman", new HashMap<String,Object>(){{
			this.put("pDate", date);
			this.put("pMan", man);
		}});
	}

	/**
	 * 保存飞书任务
	 * @param date
	 * @param man
	 */
	private void feishuTask(Timestamp date, String man, String msgcode, String name, Boolean isOK, String cust, Integer tapdID, Float hour, Timestamp updateTime, Float totalHour) {

		ProcKit.exec("yp_feishu_task", new HashMap<String,Object>(){{
			this.put("pDate", date);
			this.put("pMan", man);

			this.put("pCode", msgcode);
			this.put("pName", name);
			this.put("pIsOK", isOK);
			this.put("pCust", cust);
			this.put("pTaskID", tapdID);
			this.put("pHour", hour);
			this.put("pUpdateTime", updateTime);
			

			this.put("pTotalHour", totalHour);
		}});
	}
	

	private static Cache<String,String> MSGCACHE = CacheUtil.newTimedCache(1000*60*60*24*7);
	
	/**
	 * 处理一条消息
	 * @param createTime
	 * @param updateTime
	 * @param man
	 * @param text
	 */
	private void deal(Timestamp createTime, Timestamp updateTime, String man, String text, String msgcode) {
		if(MODE == 2) {
			this.dealMode2(createTime, updateTime, man, text, msgcode);
		}else {
			this.dealMode1(createTime, updateTime, man, text, msgcode);
			
		}
	}

	
	/**
	 * 处理一条消息
	 * @param createTime
	 * @param updateTime
	 * @param man
	 * @param text
	 */
	private void dealMode1(Timestamp createTime, Timestamp updateTime, String man, String text, String msgcode) {

		FeishuMsgBusiness feishuMsgBusiness = Aop.get(FeishuMsgBusiness.class);
		// 先解析text
		FeishuTask task = FeishuTask.parse(text);
		String m = man;
		if(task == null || task.getDate() == null || task.getTotalHour() == null) {
			log.warn("解析消息内容失败:{}", text);
			
			// 2024-12-31 F 无法解析的话回复个消息
			// 不接受消息的不发
			IProcService procService = Aop.get(IProcService.class);
			List<Record> empls = procService.yp_listempls(true);
			if(empls == null) {
				empls = new ArrayList<>();
			}
			Optional<Record> r = empls.stream().filter(new Predicate<Record>() {

				@Override
				public boolean test(Record t) {
					return m != null && m.equals(t.getStr("xName"));
				}
				
			}).findFirst();
			
			if(!MSGCACHE.containsKey(text) && (r.isPresent() && r.get().getBoolean("xIsNotice"))) {
				try {
					MSGCACHE.put(text, "");
					feishuMsgBusiness.sendToManByName(man, "解析消息失败:" + text);
				} catch (Exception e) {
					log.error(e);
				}
			}
			return;
		}
		// 能解析的话
		Timestamp date = task.getDate();
		long end = DateUtil.range(2).getTime();
		if(date == null) {
			try {

				// 2024-12-31 F 无法解析的话回复个消息
				if(!MSGCACHE.containsKey(text)) {
					MSGCACHE.put(text, "");
					feishuMsgBusiness.sendToManByName(man, "解析日期失败:" + text);
				}
			} catch (Exception e) {
				log.error(e);
			}
			return;
		}
		if(date.getTime() > end) {
			try {

				if(!MSGCACHE.containsKey(text)) {
					MSGCACHE.put(text, "");
					feishuMsgBusiness.sendToManByName(man, "解析日期异常,填报的日期超过了今天,请确认是否异常:" + text);
				}
			} catch (Exception e) {
				log.error(e);
			}
		}
		
		
		if(task.getMan() != null) {
			man = task.getMan();
		}
		// 先检查是否需要修改,如果不需要修改就直接跳过了
		Boolean ischanged = this.feishuTaskIsChanged(task.getDate(), man, updateTime);
		if(ischanged) {

			// 1.先重置这个人当天的记录
			this.clearDateMan(task.getDate(), man);
			
			// 2.再循环写入
			for(FeishuTaskItem item : task.getItems()) {
				this.feishuTask(task.getDate(), man, msgcode, item.getName(), item.getIsFinished(), item.getCust(), item.getTapdid(), item.getHour(), updateTime, task.getTotalHour());
			}
		}else {
			log.info("不需要更新:{} {}", man, text);
		}
	}

	
	/**
	 * 处理一条消息
	 * @param createTime
	 * @param updateTime
	 * @param man
	 * @param text
	 */
	private void dealMode2(Timestamp createTime, Timestamp updateTime, String man, String text, String msgcode) {
		ProcKit.exec("yp_wtask_carry_parseworkrecord", new HashMap<String,Object>(){{
			this.put("pText", text);
			this.put("pCreateTime", createTime);
			this.put("pUpdateTime", updateTime);
		}});
	}
	
	public void sendToManByName(String man, String msg) throws Exception {
		String code = "";
		if(man == null) {
			man = "";
		}
		String s = man;
		IProcService procService = Aop.get(IProcService.class);
		List<Record> empls = procService.yp_listempls(true);
		if(empls == null) {
			empls = new ArrayList<>();
		}
		Optional<Record> opt = empls.stream().filter(new Predicate<Record>() {

			@Override
			public boolean test(Record t) {
				return s.equals(t.getStr("xName"));
			}
			
		}).findFirst();
		if(opt.isPresent()) {
			code = opt.get().getStr("xCode");
		}
		if(code != null && code.length() > 0) {
			this.sendToMan(code, msg);
		}
	}

	public void sendToMan(String openid, String msg) throws Exception {
		this.send("open_id", openid, msg);
	}
	
	

	/**
	 * 发送消息到群组
	 * @param chatid
	 * @param msg
	 * @throws Exception
	 */
	public void sendToGroup(String chatid, String msg) throws Exception {
		this.send("chat_id", chatid, msg);
	}
	

	/**
	 * 
	 * @param type 消息接收者id类型 open_id/user_id/union_id/email/chat_id 
	 * @param id
	 * @param msg
	 * @throws Exception
	 */
	public void send(String type, String id, String msg) throws Exception {
		// 构建client
		Client client = Client.newBuilder(APPID, SECRET).build();
		Map<String, String> data = new HashMap<String,String>();
		data.put("text", msg);
		String jsonstr = JSONObject.toJSONString(data);
		
		// 创建请求对象
		CreateMessageReq req = CreateMessageReq.newBuilder()
			.receiveIdType(type)
			.createMessageReqBody(CreateMessageReqBody.newBuilder()
				.receiveId(id)
				.msgType("text")
				.content(jsonstr)
				.uuid(IdUtil.fastSimpleUUID())
				.build())
			.build();

		// 发起请求
		CreateMessageResp resp = client.im().message().create(req);

		// 处理服务端错误
		if(!resp.success()) {
			System.out.println(String.format("code:%s,msg:%s,reqId:%s, resp:%s",
				resp.getCode(), resp.getMsg(), resp.getRequestId(), Jsons.createGSON(true, false).toJson(JsonParser.parseString(new String(resp.getRawResponse().getBody(), StandardCharsets.UTF_8)))));
			return;
		}

		// 业务数据处理
		System.out.println(Jsons.DEFAULT.toJson(resp.getData()));
	}
}
